package cn.south.campus.business.service.impl;

import cn.south.campus.business.mapper.CommentMapper;
import cn.south.campus.business.mapper.ComplaintMapper;
import cn.south.campus.business.model.bo.LoginContext;
import cn.south.campus.business.model.bo.LoginInfo;
import cn.south.campus.business.model.entity.Complaint;
import cn.south.campus.business.model.entity.ComplaintComment;
import cn.south.campus.business.model.enums.CampusErrorEnums;
import cn.south.campus.business.model.enums.ComplaintEnums;
import cn.south.campus.business.model.req.CommentReq;
import cn.south.campus.business.model.vo.ComplaintCommentVo;
import cn.south.campus.business.service.CommentService;
import cn.south.campus.business.service.ComplaintCountService;
import cn.south.campus.common.exceptions.BusinessException;
import cn.south.campus.common.model.enums.CommonEnums;
import cn.south.campus.common.utils.AssertUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author chow
 * @since 2025-01-16
 */
@Service
@AllArgsConstructor
public class CommentServiceImpl extends ServiceImpl<CommentMapper, ComplaintComment> implements CommentService {

    private final CommentMapper commentMapper;
    //不引入service，避免循环依赖
    private final ComplaintMapper complaintMapper;

    private final ComplaintCountService complaintCountService;

    @Override
    public ComplaintCommentVo comment(ComplaintCommentVo complaintCommentVo) {
        LoginInfo context = LoginContext.getContext();
        Long complaintId = complaintCommentVo.getComplaintId();

        LambdaQueryWrapper<Complaint> queryWrapper = new LambdaQueryWrapper<Complaint>().eq(Complaint::getId, complaintId);
        Long count = complaintMapper.selectCount(queryWrapper);
        AssertUtils.isGtE(count, 1L, CommonEnums.DATA_HAS_NOT_ERROR);

        if (complaintCommentVo.getPId() != null && complaintCommentVo.getPId() != 0) {
            LambdaQueryWrapper<ComplaintComment> wrapper = new LambdaQueryWrapper<ComplaintComment>()
                    .eq(ComplaintComment::getId, complaintCommentVo.getPId());
            count = commentMapper.selectCount(wrapper);
            AssertUtils.isGtE(count, 1L, CommonEnums.DATA_HAS_NOT_ERROR);
        }
        complaintCommentVo.setUserId(context.getId());
        ComplaintComment comment = ComplaintCommentVo.convert(complaintCommentVo);
        commentMapper.insert(comment);
        complaintCommentVo.setAvatar(context.getAvatar());
        complaintCommentVo.setNickname(context.getNickname());
        complaintCommentVo.setTimeAgo("刚刚");
        complaintCommentVo.setId(comment.getId());
        return complaintCommentVo;
    }

    @Override
    public void delete(Long id) {
        LoginInfo context = LoginContext.getContext();
        ComplaintComment comment = commentMapper.selectById(id);
        AssertUtils.isNotNull(comment, CommonEnums.DATA_HAS_NOT_ERROR);
        AssertUtils.isEquals(comment.getUserId(), context.getId(), CommonEnums.NO_PERMISSION);
        commentMapper.deleteById(id);
    }

    @Override
    public IPage<ComplaintCommentVo> pageQuery(CommentReq query) {
        PageDTO<ComplaintComment> commentPageDTO = commentMapper.selectParent(query.getPageDto(), query);
        if (CollectionUtils.isEmpty(commentPageDTO.getRecords())) {
            return new PageDTO<>();
        }
        List<Long> commentIds = commentPageDTO.getRecords().stream().map(ComplaintComment::getId).collect(Collectors.toList());
        Map<Long, boolean[]> res = complaintCountService.selectMeOpt(commentIds, ComplaintEnums.BizType.COMMENT.getCode());
        Map<Long, Map<Integer, Long>> cntMap = complaintCountService.selectCount(commentIds, ComplaintEnums.BizType.COMMENT.getCode());
        Map<Long, Long> commentCntMap = cntByAncestor(commentIds);
        AtomicReference<Integer> total = new AtomicReference<>(0);
        IPage<ComplaintCommentVo> page = commentPageDTO.convert(record -> {
            ComplaintCommentVo commentVo = ComplaintCommentVo.convert(record);
            if (!CollectionUtils.isEmpty(res) && res.get(record.getId()) != null) {
                commentVo.setAgree(res.get(record.getId())[0]);
            }
            if (!CollectionUtils.isEmpty(commentCntMap)) {
                commentVo.setCommentCount(commentCntMap.getOrDefault(record.getId(), 0L));
            }
            if (!CollectionUtils.isEmpty(cntMap) && !CollectionUtils.isEmpty(cntMap.get(record.getId()))) {
                Map<Integer, Long> longMap = cntMap.get(record.getId());
                commentVo.setAgreeCount(longMap.getOrDefault(ComplaintEnums.CountType.LIKE.getCode(), 0L));
            }
            query.setAncestor(record.getId());
            //查询子评论
            PageDTO<ComplaintComment> subCommentPageDto = commentMapper.selectByAncestor(new PageDTO<>(query.getSubPageNum(), query.getSubPageSize()), query);
            if (CollectionUtils.isEmpty(subCommentPageDto.getRecords())) {
                return commentVo;
            }
            List<Long> subCommentIds = subCommentPageDto.getRecords().stream().map(ComplaintComment::getId).collect(Collectors.toList());
            Map<Long, boolean[]> subRes = complaintCountService.selectMeOpt(subCommentIds, ComplaintEnums.BizType.COMMENT.getCode());
            Map<Long, Map<Integer, Long>> subCntMap = complaintCountService.selectCount(subCommentIds, ComplaintEnums.BizType.COMMENT.getCode());
            IPage<ComplaintCommentVo> subCommentPage = subCommentPageDto.convert(item -> {
                ComplaintCommentVo subCommentVo = ComplaintCommentVo.convert(item);
                if (!CollectionUtils.isEmpty(subRes) && subRes.get(item.getId()) != null) {
                    subCommentVo.setAgree(subRes.get(item.getId())[0]);
                }
                if (!CollectionUtils.isEmpty(subCntMap) && !CollectionUtils.isEmpty(subCntMap.get(item.getId()))) {
                    Map<Integer, Long> longMap = subCntMap.get(item.getId());
                    subCommentVo.setAgreeCount(longMap.getOrDefault(ComplaintEnums.CountType.LIKE.getCode(), 0L));
                }
                return subCommentVo;
            });
            commentVo.setSubCommentsTotal(subCommentPage.getTotal());
            total.updateAndGet(v -> Math.toIntExact(v + subCommentPage.getTotal()));
            commentVo.setChildren(subCommentPage.getRecords());
            return commentVo;
        });
        page.setTotal(total.get() + page.getTotal());
        return page;
    }

    @Override
    public List<ComplaintCommentVo> listByAncestor(Long ancestor) {
        LambdaQueryWrapper<ComplaintComment> wrapper = new LambdaQueryWrapper<ComplaintComment>()
                .eq(ComplaintComment::getAncestor, ancestor)
                .orderByAsc(ComplaintComment::getCreateTime);
        List<ComplaintComment> commentList = commentMapper.selectList(wrapper);
        return commentList.stream().map(ComplaintCommentVo::convert).collect(Collectors.toList());
    }

    public Map<Long, Long> cntByAncestor(List<Long> ancestors) {
        String ancestor = "ancestor";
        QueryWrapper<ComplaintComment> wrapper = new QueryWrapper<ComplaintComment>()
                .in(ancestor, ancestors)
                .groupBy(ancestor)
                .select(ancestor, "count(*) as count");
        List<Map<String, Object>> mapList = commentMapper.selectMaps(wrapper);
        return mapList.stream().collect(Collectors.toMap(map -> (Long) map.get(ancestor), map -> (Long) map.get("count")));
    }

    @Override
    public Map<Long, Long> selectCount(List<Long> complaintIds) {
        if (CollectionUtils.isEmpty(complaintIds)) {
            return Collections.emptyMap();
        }
        String complaintId = "complaint_id";
        QueryWrapper<ComplaintComment> wrapper = new QueryWrapper<ComplaintComment>()
                .in(complaintId, complaintIds)
                .groupBy(complaintId)
                .select(complaintId, "count(*) as count");
        List<Map<String, Object>> mapList = commentMapper.selectMaps(wrapper);
        return mapList.stream().collect(Collectors.toMap(map -> (Long) map.get(complaintId), map -> (Long) map.get("count")));
    }

    @Override
    public void agreeComment(Long id) {
        Long userId = LoginContext.getContext().getId();
        LambdaQueryWrapper<ComplaintComment> wrapper = new LambdaQueryWrapper<ComplaintComment>()
                .eq(ComplaintComment::getId, id);
        ComplaintComment comment = commentMapper.selectOne(wrapper);
        if (comment.getUserId().equals(userId)) {
            throw new BusinessException(CampusErrorEnums.COMPLAINT_NOT_AGREE_SELF);
        }
        complaintCountService.cntSave(id, ComplaintEnums.CountType.LIKE.getCode(), ComplaintEnums.BizType.COMMENT.getCode());
    }

    @Override
    public ComplaintCommentVo getComment(Long id) {
        ComplaintComment comment = commentMapper.selectById(id);
        ComplaintCommentVo commentVo = ComplaintCommentVo.convert(comment);
        Map<Integer, Long> longMap = complaintCountService.selectCount(commentVo.getId(), ComplaintEnums.BizType.COMMENT.getCode());
        boolean[] booleans = complaintCountService.selectMeOpt(commentVo.getId(), ComplaintEnums.BizType.COMMENT.getCode());
        commentVo.setAgree(booleans[0]);
        if (!CollectionUtils.isEmpty(longMap)) {
            commentVo.setAgreeCount(longMap.getOrDefault(ComplaintEnums.CountType.LIKE.getCode(), 0L));
        }
        return commentVo;
    }

    @Override
    public IPage<ComplaintCommentVo> pageByAncestor(CommentReq query) {
        //查询子评论
        PageDTO<ComplaintComment> subCommentPageDto = commentMapper.selectByAncestor(query.getPageDto(), query);
        if (CollectionUtils.isEmpty(subCommentPageDto.getRecords())) {
            return new PageDTO<>();
        }
        List<Long> subCommentIds = subCommentPageDto.getRecords().stream().map(ComplaintComment::getId).collect(Collectors.toList());
        Map<Long, boolean[]> subRes = complaintCountService.selectMeOpt(subCommentIds, ComplaintEnums.BizType.COMMENT.getCode());
        Map<Long, Map<Integer, Long>> subCntMap = complaintCountService.selectCount(subCommentIds, ComplaintEnums.BizType.COMMENT.getCode());
        return subCommentPageDto.convert(item -> {
            ComplaintCommentVo subCommentVo = ComplaintCommentVo.convert(item);
            if (!CollectionUtils.isEmpty(subRes) && subRes.get(item.getId()) != null) {
                subCommentVo.setAgree(subRes.get(item.getId())[0]);
            }
            if (!CollectionUtils.isEmpty(subCntMap) && !CollectionUtils.isEmpty(subCntMap.get(item.getId()))) {
                Map<Integer, Long> longMap = subCntMap.get(item.getId());
                subCommentVo.setAgreeCount(longMap.getOrDefault(ComplaintEnums.CountType.LIKE.getCode(), 0L));
            }
            return subCommentVo;
        });
    }
}
